/***************************************************************************
 *	@file 		fetion_user.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of operation about user
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_user.h"
#include "fetion_database.h"
#include "fetion_exception.h"
#include "fetion_config.h"
#include "fetion_contact.h"
#include "fetion_sip.h"
#include "fetion_login.h"
#include "fetion_chat.h"

extern std::map<int, std::string> state_map;
extern boost::mutex user_mutex;
extern boost::mutex cin_mutex;
extern bool send_msg;
extern boost::condition_variable cin_cond;

int userlist::create_table_in_db()
{
	int ret = FUNC_SUCC;
	std::string SQL;
	SQLiteWrapper sqlite;
	
	try {
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQL = "create table userlist (no, password, laststate, islastuser,userid,sid);";
		if(!sqlite.DirectStatement(SQL)) {
			throw fetionException("excute SQL statement failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "create table userlist in database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

/*--------------------------------------------------------------------
 * @brief : load user information list from local database
 * @returns : return state
 *-------------------------------------------------------------------*/
int userlist::load_from_database()
{
	int ret = FUNC_SUCC;
	
	try {
		SQLiteWrapper sqlite;
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQLiteWrapper::ResultTable res;
		if(!sqlite.SelectStmt("select * from userlist;", res)) {
			if(FUNC_SUCC != create_table_in_db()) {
				throw fetionException("can't create table userlist in database.");
			}
			if(!sqlite.SelectStmt("select * from userlist;", res)) {
				throw fetionException("query in table userlist failed.");
			}
		}
		SQLiteWrapper::ResultTable::rt_iter rtiter;

		for(rtiter = res.records_.begin(); rtiter != res.records_.end(); ++rtiter) {
			if(rtiter->fields_.size() != item_num) {
				throw fetionException("query result didn't match data structure.");
			}
			else {
				userinfo uinfo;
				uinfo.userNo 		= rtiter->fields_[0];
				uinfo.passwd 		= rtiter->fields_[1];
				uinfo.lastState 	= atoi(rtiter->fields_[2].c_str());
				uinfo.isLastUser 	= atoi(rtiter->fields_[3].c_str());
				uinfo.userId 		= rtiter->fields_[4];
				uinfo.sId			= rtiter->fields_[5];
				//WRITE_LOG(LOG_TRACE, "load login infomation of user : %s", uinfo.userNo.c_str());
				ulist.push_back(uinfo);
			}
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "load userlist from database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

void userlist::print()
{
	int item_no = 1;
	std::vector<userinfo>::iterator iter;
	char haspasswd[4];

	sysinfoString sstr("-----------------------------------------------------------------\n"
					   "index     phone number    has password  last state  fetion number\n");
	stdout<<sstr;
	for(iter = ulist.begin(); iter != ulist.end(); ++iter, ++item_no) {
		if(iter->passwd.empty()) 
			strcpy(haspasswd, "no");
		else 
			strcpy(haspasswd, "yes");
		sstr("  %d       %s         %s       %-10s    %s\n", 
			item_no, iter->userNo.c_str(), haspasswd,
			state_map[iter->lastState].c_str(), iter->sId.c_str());
		stdout<<sstr;
	}
	sstr("-----------------------------------------------------------------\n");
	stdout<<sstr;
}

userlist::userinfo *userlist::find_by_userNo(const std::string &no)
{
	std::vector<userinfo>::iterator iter;
	if((iter = find_if(ulist.begin(), ulist.end(), equal_to_userNo(no))) != ulist.end()) {
		return &(*iter);
	}
	return NULL;
}

int userlist::update(userinfo &ui)
{
	int ret = FUNC_SUCC;
	std::stringstream SQL;

	try {
		SQLiteWrapper sqlite;
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQL<<"delete from userlist where no = \""<<ui.userNo<<"\"";
		sqlite.DirectStatement(SQL.str());

		SQL.str("");
		SQL<<"insert into userlist values('"<<
			ui.userNo << "','" << ui.passwd << "'," << 
			ui.lastState << ","<< ui.isLastUser << ",'" << 
			ui.userId << "','" << ui.sId << "');";
		if(!sqlite.DirectStatement(SQL.str())) {
			throw fetionException("insert to userlist failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "update userlist in database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

currentUser::~currentUser() 
{
	if(NULL != ver) {
		delete ver;	ver = NULL;
	}
	if(NULL != config) config = NULL;
	if(NULL != sip) {
		delete sip; sip = NULL;
	}
	if(NULL != applyList) {
		delete applyList; applyList = NULL;
	}
	if(NULL != recentMessage) {
		delete recentMessage; recentMessage = NULL;
	}
	if(NULL != currentConv) {
		delete currentConv; currentConv = NULL;
	}
}

void currentUser::parse_ssi_succ(const xmlpp::Node *pNode)
{
	const xmlpp::Element *elem = dynamic_cast<const xmlpp::Element*>(pNode);

	sipuri = elem->get_attribute_value("uri");
	sId = get_sid_from_sipuri(sipuri);
	if(mobileno != elem->get_attribute_value("mobile-no")) {
		throw fetionException("get mobile no from ssi response is different from user mobile no.");
	}
	userId = elem->get_attribute_value("user-id");
	if(sipuri.empty() || sId.empty() || userId.empty()) {
		WRITE_LOG(LOG_WARNING, "parse xml of ssi response get nothing.");
	}
}

void currentUser::parse_ssi_fail(const xmlpp::Node *pNode)
{
	ver = new Verification;
	if(NULL == ver) {
		throw fetionException("memory allocation for verification failed.");
	}

	const xmlpp::Element *elem = dynamic_cast<const xmlpp::Element*>(pNode);
	ver->algorithm = elem->get_attribute_value("algorithm");
	ver->type      = elem->get_attribute_value("type");
	ver->text      = elem->get_attribute_value("text");
	ver->tips	  = elem->get_attribute_value("tips");
	
	if(ver->algorithm.empty() || ver->type.empty()
			|| ver->text.empty() || ver->tips.empty()) {
		WRITE_LOG(LOG_WARNING, "parse xml of ssi response get nothing.");
	}
}

void currentUser::get_userinfo(userlist::userinfo &ui)
{
	ui.userNo 		= mobileno;
	ui.passwd 		= password;
	ui.userId 		= userId;
	ui.sId			= sId;
	ui.isLastUser 	= 1;
	ui.lastState 	= state;
}

int currentUser::create_table_in_db()
{
	int ret = FUNC_SUCC;
	std::string SQL;
	SQLiteWrapper sqlite;
	
	try {
		if(!sqlite.Open(config->db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQL = 	"create table user ("
				"sId,userId,mobileno,password,sipuri,"
				"publicIp,lastLoginIp,lastLoginTime,"
				"personalVersion, contactVersion,"
				"nickname,impression,country,province,"
				"city,gender,smsOnLineStatus,"
				"customConfigVersion, customConfig,"
				"boundToMobile);";
		
		if(!sqlite.DirectStatement(SQL)) {
			throw fetionException("excute SQL statement failed.");
		}
	}
	catch(char *c) {
		WRITE_LOG(LOG_DEBUG, "create table user in database failed : %s", c);
		ret = FUNC_FAIL;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "create table user in database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int currentUser::load_from_database()
{
	int ret = FUNC_SUCC;
	SQLiteStatement *stmt = NULL;
	SQLiteWrapper sqlite;

	try {
		if(!sqlite.Open(config->db_path)) {
			throw fetionException("can't open global local database.");
		}

		stmt = sqlite.Statement("select * from user;");
		if(NULL == stmt) {
			if(FUNC_SUCC != create_table_in_db()) {
				throw fetionException("can't create table user in database.");
			}
			stmt = sqlite.Statement("select * from user;");
			if(NULL == stmt) {
				throw fetionException("generate SQL statement object failed.");
			}
		}
		if(!stmt->NextRow()) {
			return ret;
		}
		
		if(sId != stmt->ValueString(0)) 
			WRITE_LOG(LOG_TRACE, "fetion number has changed since last login.");
		if(userId != stmt->ValueString(1)) 
			WRITE_LOG(LOG_TRACE, "user ID has changed since last login.");
		if(mobileno != stmt->ValueString(2))
			WRITE_LOG(LOG_TRACE, "mobile number has changed since last login.");
		if(password	!= stmt->ValueString(3))
			WRITE_LOG(LOG_TRACE, "password has changed since last login.");
		if(sipuri != stmt->ValueString(4))
			WRITE_LOG(LOG_TRACE, "sip uri has changed since last login.");
		
		publicIp			= stmt->ValueString(5);
		lastLoginIp			= stmt->ValueString(6);
		lastLoginTime		= stmt->ValueString(7);
		personalVersion		= stmt->ValueString(8);
		contactVersion		= stmt->ValueString(9);
		nickname 			= stmt->ValueString(10);
		impression 			= stmt->ValueString(11);
		country				= stmt->ValueString(12);
		province			= stmt->ValueString(13);
		city				= stmt->ValueString(14);
		gender 				= stmt->ValueInt(15);
		smsOnLineStatus 	= stmt->ValueString(16);
		customConfigVersion = stmt->ValueString(17);
		customConfig 		= stmt->ValueString(18);
		boundToMobile 		= stmt->ValueInt(19);
		
		if(stmt->NextRow()) {
			throw fetionException("query more than one result in table user.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "load user information from database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	if(NULL != stmt) {
		delete stmt; stmt = NULL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "load user information from database.");
	}
	return ret;
	
}

int currentUser::save_to_database()
{
	int ret = FUNC_SUCC;
	SQLiteWrapper sqlite;
	std::stringstream SQL;
	bool retry = false;

	try {
		if(!sqlite.Open(config->db_path)) {
			throw fetionException("can't open global local database.");
		}
		do {
			if(!sqlite.DirectStatement("delete from user;")) {
				if(FUNC_SUCC != create_table_in_db()) {
					throw fetionException("create table user failed.");
				}
			}
			sqlite.DirectStatement(";");
			SQL<<"insert into user values ('"
			   <<sId<<"','"<<userId<< "','"<<mobileno<<"','"
			   <<password<<"','"<<sipuri<<"','"<<publicIp<<"','"
			   <<lastLoginIp<<"','"<<lastLoginTime<<"','"
			   <<personalVersion<<"','"<<contactVersion<<"','"
			   <<nickname<<"','"<<impression<<"','"<<country<<"','"
			   <<province<<"','"<<city<<"',"<<gender<<",'"
			   <<smsOnLineStatus<<"','"<<customConfigVersion<<"','"
			   <<customConfig<<"',"<<boundToMobile<<");";

//			std::cout<<"---------------"<<std::endl<<SQL.str()<<std::endl;			
			if(!sqlite.DirectStatement(SQL.str())) {
/*				if(!sqlite.DirectStatement("drop table user;")) {
					throw fetionException("drop table user failed.");
				}
*/				if(retry) {
					throw fetionException("insert to table user failed.");
				} else {
					retry = true;
					SQL.str("");
					continue;
				}
			}
			break;
		}while(true);
	}
	catch(char *c) {
		WRITE_LOG(LOG_FATAL, "save user to database failed : %s.", c);
		ret = FUNC_FAIL;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "save user to database failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "save user information to database.");
	}
	return ret;
}

int currentUser::load_contacts_from_db()
{
	int ret = FUNC_SUCC;
	SQLiteStatement *stmt = NULL;
	SQLiteWrapper sqlite;
	std::string SQL;
	Group group;
	Contact contact;

	try {
		if(!sqlite.Open(config->db_path)) {
			throw fetionException("can't open global local database.");
		}

		stmt = sqlite.Statement("select * from groups order by groupid;");
		if(NULL == stmt) {
			SQL = "create table groups (groupid, groupname);";
			if(!sqlite.DirectStatement(SQL)) {
				throw fetionException("create talbe groups failed.");
			}
			stmt = sqlite.Statement("select * from groups order by groupid;");
			if(NULL == stmt) {
				throw fetionException("generate SQL statement object failed."); 
			}
		}
		groupList.clear();
		while(stmt->NextRow()) {
			group.load_from_database(stmt);
			groupList.push_back(group);
		}
		
		stmt = sqlite.Statement("select * from contacts;");
		if(NULL == stmt) {
			SQL =   "create table contacts (userId,"
					"sId,sipuri,localname,nickname,"
					"impression,mobileno,devicetype,"
					"portraitCrc,birthday,country,"
					"province,city,identity,scoreLevel,"
					"serviceStatus,carrierStatus,"
					"relationStatus,carrier,groupid,gender);";
			if(!sqlite.DirectStatement(SQL)) {
				throw fetionException("create talbe contacts failed.");
			}
			stmt = sqlite.Statement("select * from contacts;");
			if(NULL == stmt) {
				throw fetionException("generate SQL statement object failed.");
			}
		}
		contactList.clear();
		while(stmt->NextRow()) {
			contact.load_from_database(stmt);
			contactList.push_back(contact);
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "load contacts from database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	if(NULL != stmt) {
		delete stmt; stmt = NULL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "load contacts information from database.");
	}
	return ret;
}

int currentUser::save_contacts_to_db()
{
	int ret = FUNC_SUCC;
	SQLiteWrapper sqlite;
	std::string SQL;
	bool retry = false;

	try {
		if(!sqlite.Open(config->db_path)) {
			throw fetionException("can't open global local database.");
		}
		if(!sqlite.Begin()) {
			throw fetionException("begin transcation failed.");
		}
		do {
			if(!sqlite.DirectStatement("delete from groups;")) {
				if(!sqlite.DirectStatement("create table groups (groupid,groupname)")) {
					throw fetionException("create table groups failed.");
				}
			}

			for(std::list<Group>::iterator iter = groupList.begin(); 
					iter != groupList.end(); ++iter) {
				if(!iter->save_to_database(sqlite)) {
					if(!sqlite.DirectStatement("drop table groups;")) {
						throw fetionException("drop table groups failed.");
					}
					if(retry) {
						throw fetionException("insert to table groups failed.");
					} else {
						retry = true;
						continue;
					}
				}
			}
			break;
		}while(true);
		retry = false;
		do {
			if(!sqlite.DirectStatement("delete from contacts;")) {
				SQL =   "create table contacts (userId,"
						"sId,sipuri,localname,nickname,"
						"impression,mobileno,devicetype,"
						"portraitCrc,birthday,country,"
						"province,city,identity,scoreLevel,"
						"serviceStatus,carrierStatus,"
						"relationStatus,carrier,groupid,gender);";
				if(!sqlite.DirectStatement(SQL)) {
					throw fetionException("create talbe contacts failed.");
				}
			}

//			std::cout<<contactList.size()<<std::endl;
			for(std::list<Contact>::iterator iter = contactList.begin(); 
					iter != contactList.end(); ++iter) {
				if(!iter->save_to_database(sqlite)) {
					if(!sqlite.DirectStatement("drop table contacts;")) {
						throw fetionException("drop table contacts failed.");
					}
					if(retry) {
						throw fetionException("insert to table contacts failed.");
					} else {
						retry = true;
						continue;
					}
				}
			}
			break;
		}while(true);
		if(!sqlite.End()) {
			throw fetionException("end transcation failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "save groups and contacts to database failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "save groups and contacts list to database.");
	}
	return ret;
}

int currentUser::get_sip_auth_body(std::string &body) const
{
	int ret = FUNC_SUCC;
	std::string basexml = "<args></args>";
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *rootnode = NULL, *node = NULL;
	xmlpp::Element *elem;
	std::stringstream strstr;

	try {
		parser.parse_memory(basexml);
		doc = parser.get_document();
		rootnode = doc->get_root_node();
		
		elem = rootnode->add_child("device");
		elem->set_attribute("machine-code", "001676C0E351");

		elem = rootnode->add_child("caps");
		elem->set_attribute("value", "1ff", "");

		elem = rootnode->add_child("events");
		elem->set_attribute("value", "7f", "");

		elem = rootnode->add_child("user-info");
		elem->set_attribute("mobile-no", mobileno);
		elem->set_attribute("user-id", userId);

		node = parser.get_specify_node(rootnode, "user-info");
		if(NULL == node) {
			throw fetionException("get node user-info failed.");
		}
		elem = node->add_child("personal");
		elem->set_attribute("version", personalVersion);
		elem->set_attribute("attributes", "v4default");

		elem = node->add_child("custom-config");
		elem->set_attribute("version", customConfigVersion);

		elem = node->add_child("contact-list");
		elem->set_attribute("version", contactVersion);
		elem->set_attribute("buddy-attributes", "v4default");

		elem = rootnode->add_child("credentials");
		elem->set_attribute("domains", "fetion.com.cn", "");

		elem = rootnode->add_child("presence");
		node = parser.get_specify_node(rootnode, "presence");/////
		if(NULL == node) {
			throw fetionException("get node presence failed.");
		}
		strstr<<state;
		elem = node->add_child("basic");
		elem->set_attribute("value", strstr.str());
		elem->set_attribute("desc", "");

		body = doc->write_to_string();
		xml_convert(body);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse configuration xml failed : %s.", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

int currentUser::parse_add_buddy_verification(const std::string &sipres)
{
	int ret = FUNC_SUCC;
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *node;
	xmlpp::Element *elem;
	std::string tmp1, tmp2;
	std::string::size_type pos;
	
	try {
		sip->get_attribute_value(sipres, "W", tmp1);
		if((pos = tmp1.find("algorithm=", 0)) == std::string::npos) {
			throw fetionException("can't get algorithm.");
		}
		tmp2 = tmp1.substr(pos + 11, tmp1.length() - pos - 11);
		ver->algorithm = tmp2.substr(0, tmp2.find("\"", 0));
		if((pos = tmp1.find("type=", 0)) == std::string::npos) {
			throw fetionException("can't get type.");
		}
		tmp2 = tmp1.substr(pos + 6, tmp1.length() - pos - 6);
		ver->type = tmp2.substr(0, tmp2.find("\"", 0));

		if((pos = sipres.find("\r\n\r\n", 0)) == std::string::npos) {
			throw fetionException("can't get xml.");
		}
		tmp1 = sipres.substr(pos+4, sipres.length()-pos-4);
		parser.parse_memory(tmp1);
		doc = parser.get_document();
		node = doc->get_root_node();
		xmlpp::Node::NodeList children_list = node->get_children();
		elem = dynamic_cast<xmlpp::Element *>(*(children_list.begin()));
		ver->text = elem->get_attribute_value("text");
		ver->tips = elem->get_attribute_value("tips");
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse add buddy verfication failed : %s.", ex.what());	
		ret = FUNC_FAIL;
	}
	return ret;
}

int currentUser::parse_personal_info(xmlpp::Node *pNode)
{
	xmlpp::Element *elem = dynamic_cast<xmlpp::Element *>(pNode);
	std::string tmp;
	std::string::size_type pos;

	personalVersion = elem->get_attribute_value("version");
	tmp = elem->get_attribute_value("sid");
	if(!tmp.empty()) {
		sId = tmp;
	}

	tmp.clear();
	tmp = elem->get_attribute_value("mobile-no");
	if(!tmp.empty()) {
		boundToMobile = BOUND_MOBILE_ENABLE;
		mobileno = tmp;
	}
	else {
		boundToMobile = BOUND_MOBILE_DISABLE;
	}

	tmp.clear();
	tmp = elem->get_attribute_value("carrier-status");
	if(!tmp.empty()) {
		carrierStatus = atoi(tmp.c_str());
	}
	tmp.clear();
	tmp = elem->get_attribute_value("nickname");
	if(!tmp.empty()) {
		nickname = tmp;
	}
	tmp.clear();
	tmp = elem->get_attribute_value("gender");
	if(!tmp.empty()) {
		gender = atoi(tmp.c_str());
	}
	tmp.clear();
	tmp = elem->get_attribute_value("sms-online-status");
	if(!tmp.empty()) {
		smsOnLineStatus = tmp.c_str();
	}
	tmp.clear();
	tmp = elem->get_attribute_value("impresa");
	if(!tmp.empty()) {
		impression = tmp.c_str();
	}
	tmp.clear();
	tmp = elem->get_attribute_value("carrier-region");
	if(!tmp.empty()) {
		pos = tmp.find(".", 0);
		country = tmp.substr(0, pos);
		province = tmp.substr(pos+1, tmp.length()-pos-1);
		pos = province.find(".", 0);
		city = province.substr(pos+1, province.length()-pos-1);
		province = province.substr(0, pos);
		pos = city.find(".", 0);
		city = city.substr(0, pos);
	}
	return FUNC_SUCC;
}

int currentUser::parse_contact_list(xmlpp::Node *pNode)
{
	int ret = FUNC_SUCC;
	Contact contact, *pc;
	Group group, *pg;
	xmlParser parser;
	xmlpp::Node *node;
	xmlpp::Element *elem, *elem1;
	std::string tmp;
	bool has_group = true, has_contact = true;
	
	try {
		elem = dynamic_cast<xmlpp::Element*>(pNode);
		tmp = elem->get_attribute_value("version");
		if(contactVersion == tmp) {
			WRITE_LOG(LOG_TRACE, "contact list is the same as that on local disk.");
			return ret;
		}
		if(!tmp.empty()) {
			contactVersion = tmp;
		}
		groupCount = 0;
		node = parser.get_specify_node(pNode, "buddy-lists");
		if(NULL == node) {
			throw fetionException("parse group failed.");
		}
		xmlpp::Node::NodeList children_list = node->get_children();
		xmlpp::Node::NodeList::const_iterator iter;
//		std::cout<<"name"<<std::endl;
		for(iter = children_list.begin(); iter != children_list.end(); ++iter) {
			has_group = true;
			elem1 = dynamic_cast<xmlpp::Element *>(*iter);
			tmp.clear();
			tmp = elem1->get_attribute_value("id");
			if(tmp.empty()) {
				continue;
			}
			pg = find_group_by_id(atoi(tmp.c_str()));
			if(NULL == pg) {
				has_group = false;
				pg = &group;
				group.dirty = 0;
			}
			pg->groupId = atoi(tmp.c_str());
			pg->groupName = elem1->get_attribute_value("name");
			groupCount++;
			pg->dirty = 1;
			if(!has_group) {
				groupList.push_back(*pg);
				has_group = true;
			}
		}

		contactCount = 0;
		node = parser.get_specify_node(pNode, "buddies");
		if(NULL == node) {
			throw fetionException("parse contact failed.");
		}
		children_list = node->get_children();
		for(iter = children_list.begin(); iter != children_list.end(); ++iter) {
			contact.clear();
			has_contact = true;
			tmp.clear();
			elem1 = dynamic_cast<xmlpp::Element *>(*iter);
			tmp = elem1->get_attribute_value("i");
			if(tmp.empty()) {
				continue;
			}
			pc = find_contact_by_userid(tmp);
			if(NULL == pc) {
				has_contact = false;
				pc = &contact;
				contact.dirty = 0;
			}
			pc->userId = tmp;
			if(1 == pc->dirty) {
				continue;
			}
			contactCount++;
			pc->dirty = 1;
			
			tmp.clear();
			tmp = elem1->get_attribute_value("n");
			if(!tmp.empty()) {
				pc->localname = tmp;
				tmp.clear();
			}
			tmp = elem1->get_attribute_value("l");
			if(!tmp.empty()) {
				pc->groupid = atoi(tmp.c_str());
				if(tmp.find(":", 0) != std::string::npos || pc->groupid < 0) {
					pc->groupid = 0;
				}
				tmp.clear();
			}
			tmp = elem1->get_attribute_value("p");
			if(!tmp.empty()) {
				if(tmp.find("identity=1", 0) != std::string::npos) {
					pc->identity = 1;
				}
				else {
					pc->identity = 0;
				}
				tmp.clear();
			}
			tmp = elem1->get_attribute_value("r");
			if(!tmp.empty()) {
				pc->relationStatus = atoi(tmp.c_str());
				tmp.clear();
			}
			tmp = elem1->get_attribute_value("u");
			if(!tmp.empty()) {
				pc->sipuri = tmp;
				tmp.clear();
			}
			pc->portraitCrc = "0";
			if(!has_contact) {
				contactList.push_back(*pc);
				has_contact = true;
			}
		}
//		std::cout<<contactList.size()<<std::endl;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse contact list failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int currentUser::parse_stranger_list(xmlpp::Node *node)
{
	xmlpp::Node::NodeList children_list = node->get_children();
	xmlpp::Node::NodeList::iterator iter;
	xmlpp::Element *elem;
	std::string buf;
	Contact contact, *pc = NULL;
	int hasBuddy;

	for(iter = children_list.begin(); iter != children_list.end(); ++iter) {
		contact.clear();
		hasBuddy = 1;
		contactCount++;
		elem = dynamic_cast<xmlpp::Element *>(*iter);
		buf = elem->get_attribute_value("i");
		pc = find_contact_by_userid(buf);
		if(pc == NULL) {
			hasBuddy = 0;
			pc = &contact;
		}
		pc->userId = buf;
		buf.clear();
		buf = elem->get_attribute_value("u");
		pc->sipuri = buf;
		pc->groupid = BUDDY_LIST_STRANGER;
		pc->dirty = 1;
		if(hasBuddy == 0) {
			contactList.push_back(*pc);
			hasBuddy = 1;
		}
	}
	return FUNC_SUCC;
}

int currentUser::parse_sms_frequency(xmlpp::Node *node)
{
	std::string res;
	xmlpp::Node::NodeList children_list = node->get_children();
	xmlpp::Element *elem = dynamic_cast<xmlpp::Element *>(*children_list.begin());

	res = elem->get_attribute_value("day-limit");
	if(!res.empty()) {
		smsDayLimit = atoi(res.c_str());
		res.clear();
	}
	res = elem->get_attribute_value("day-count");
	if(!res.empty()) {
		smsDayCount = atoi(res.c_str());
		res.clear();
	}
	res = elem->get_attribute_value("month-limit");
	if(!res.empty()) {
		smsMonthLimit = atoi(res.c_str());
		res.clear();
	}
	res = elem->get_attribute_value("month-count");
	if(!res.empty()) {
		smsMonthCount = atoi(res.c_str());
		res.clear();
	}
	return FUNC_SUCC;
}

/*int currentUser::parse_presence_body(const std::string &body)
{
	int ret = FUNC_SUCC;
	xmlParser parser;
	xmlpp::Element *elem;
	xmlpp::Node *node, *node1;
	std::string tmp;
	Contact *currentContact = NULL;
	
	try {
		parser.parse_memory(xml);
		node = parser.get_specify_node_from_root("c");
		while(NULL != node) {
			elem = dynamic_cast<xmlpp::Element *>(node);
			tmp = elem->get_attribute_value("id");
			currentContact = find_contact_by_userid(tmp);
			if(NULL == currentContact) {
				WRITE_LOG(LOG_WARNING, "sip presence contact is not buddy of current user.");
				node = node->get_next_sibling();
				continue;
			}
			node1 = parser.get_specify_node(node, "p");
			elem = dynamic_cast<xmlpp::Element *>(node1);
			if(FUNC_SUCC != currentContact->parse_contact_from_presence_p(elem)) {
				throw fetionException("parse contact from presence 'p' failed.");
			}
			node1 = parser.xml_goto_node(node, "pr");
			elem = dynamic_cast<xmlpp::Element *>(node1);
			if(FUNC_SUCC != currentContact->parse_contact_from_presence_p(elem)) {
				throw fetionException("parse contact from presence 'pr' failed.");
			}
			node = node->get_next_sibling();
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse contact list failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;

}*/

Group *currentUser::find_group_by_id(const int id)
{
	std::list<Group>::iterator iter;
	if((iter = find_if(groupList.begin(), groupList.end(), Group::equal_by_id(id))) != groupList.end()) {
		return &(*iter);
	}
	return NULL;
}

Contact *currentUser::find_contact_by_userid(const std::string &userid)
{
	std::list<Contact>::iterator iter;
	if((iter = find_if(contactList.begin(), contactList.end(), Contact::equal_by_userid(userid))) 
			!= contactList.end()) {
		return &(*iter);
	}
	return NULL;
}

Contact *currentUser::find_contact_by_groupid(const int group_id)
{
	std::list<Contact>::iterator iter;
	if((iter = find_if(contactList.begin(), contactList.end(), Contact::equal_by_groupid(group_id))) 
			!= contactList.end()) {
		return &(*iter);
	}
	return NULL;
}

Contact *currentUser::find_contact_by_sipuri(const std::string &sip_uri)
{
	std::list<Contact>::iterator iter;
	if((iter = find_if(contactList.begin(), contactList.end(), Contact::equal_by_sipuri(sip_uri))) 
			!= contactList.end()) {
		return &(*iter);
	}
	return NULL;
}

Contact *currentUser::find_contact_by_string(const std::string &str)
{
	std::list<Contact>::iterator iter;
	if((iter = find_if(contactList.begin(), contactList.end(), Contact::equal_by_contact_string(str))) 
			!= contactList.end()) {
		return &(*iter);
	}
	return NULL;
}

Contact *currentUser::find_tmp_contact_by_string(const std::string &str)
{
	std::list<Contact>::iterator iter;
	if((iter = find_if(tmp_contactList.begin(), tmp_contactList.end(), Contact::equal_by_contact_string(str))) 
			!= tmp_contactList.end()) {
		return &(*iter);
	}
	return NULL;
}

int currentUser::handle_add_buddy_request(Command &cmd)//const int index, bool result)
{
	std::string body;
	std::string sipstr;
	int ret = FUNC_SUCC;

	boost::mutex::scoped_lock lock(user_mutex);		
	try {
		if(cmd.paraIndex < 0 || cmd.paraIndex >= applyList->size()) {
			errorString estr("there is no add buddy request of index %d.\n", cmd.paraIndex);
			stdout<<estr;
			return ret;
		}
		sip->setType(FetionSip::SIP_SERVICE);
		sip->addEventHeader(SIP_EVENT_HANDLECONTACTREQUEST);
		body = applyList->generate_handle_body(cmd.paraIndex, cmd.addBuddyResult);
		if(body.empty()) {
			throw fetionException("generate sip body failed.");
		}
		if(FUNC_SUCC != sip->get_send_string(sipstr, body)) {
			throw fetionException("get sip sending string failed.");
		}
		sip->send_sip_message(sipstr);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "handle add buddy request failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int currentUser::send_message_to_contact(Command &cmd)
{
	Contact *contact;
	int ret = FUNC_SUCC;
//	std::string message;
	boost::mutex::scoped_lock user_lock(user_mutex);	// mutex for user information
//	boost::mutex::scoped_lock cin_lock(cin_mutex);
	
	try {
		// user input command "r", send to response recent received message
		if(Command::CMD_SEND_TO_RECENT_CONTACT == cmd.choiceIndex) {
			contact = recentMessage->contact;
			// no received message
			if(NULL == contact) {
				errorString estr("no recent message to response.\n");
				stdout<<estr;
				return ret;
			}
		}
		// user input command "t x", send to contact "x".
		else if(!cmd.paraString.empty()) {
			// it's the second running of this function after user enter an send message command.
			// this time, function is running in a new thread which is created by sip listener
			// after get contact information sip.
			if(send_msg == true) {
				// find contact in tmporary contact list which is for contact who is not buddy of user
				contact = find_tmp_contact_by_string(cmd.paraString);
				// if there is no such contact in tmporary contact list, return to main loop.
				if(NULL == contact) {
					errorString estr("can't find contact in contact list.\n");
					sysinfoString sstr("> ");
					stdout<<estr<<sstr;
					send_msg= false;	// this thread will end, set send_msg to false
					cin_cond.notify_one(); // notiry main loop to continue
					return ret;
				}
				if(RELATION_STATUS_UNAUTHENTICATED == contact->relationStatus) {
					errorString estr("relation status of contact is unauthenticated.\n");
					sysinfoString sstr("> ");
					stdout<<estr<<sstr;
					send_msg= false;	// this thread will end, set send_msg to false
					cin_cond.notify_one(); // notiry main loop to continue
					return ret;
				}
				if(BASIC_SERVICE_ABNORMAL == contact->serviceStatus && 
						CARRIER_STATUS_CLOSED == contact->carrierStatus) {
					errorString estr("fetion service of contact has shut down.\n");
					sysinfoString sstr("> ");
					stdout<<estr<<sstr;
					send_msg= false;	// this thread will end, set send_msg to false
					cin_cond.notify_one(); // notiry main loop to continue
					return ret;
				}
			}
			// it's the first running of this function after user enter an send message command
			else {
				// find contact in contact list first
				contact = find_contact_by_string(cmd.paraString);
				if(NULL == contact) {	// can't find contact in contact list
					// send message to contact who is not in contact list
					// get information of this contact first.
					if(FUNC_SUCC != get_contact_information_by_no(cmd.paraString)) {
						// get contact information failed, return without sending message
						errorString estr("get information of contact failed.\n");
						stdout<<estr;
						return ret;
					}
					// set send_msg true, when listener get contact information sip, 
					// there will be a thread created to send message, which run this function
					send_msg = true;	
					return ret;	// return to main input loop, main input loop will wait send message thread finished.
				}
				// contact is unauthenticated
				if(RELATION_STATUS_UNAUTHENTICATED == contact->relationStatus) {
					errorString estr("relation status of contact is unauthenticated.\n");
					stdout<<estr;
					return ret;
				}
				// contact does not have service
				if(BASIC_SERVICE_ABNORMAL == contact->serviceStatus && 
						CARRIER_STATUS_CLOSED == contact->carrierStatus) {
					errorString estr("fetion service of contact has shut down.\n");
					stdout<<estr;
					return ret;
				}
			}
		}
		// user input command "t", send message to last contact
		else {
			if(0 == cmd.paraIndex) {
				contact = NULL;
			}
			else {
				errorString estr("no contact was chosen.\n");
				return ret;
			}
		}

		// use contact to change conversation
		currentConv->new_conversation(contact, sip);
		user_lock.unlock();	// let listening thread continue
		if(FUNC_SUCC != currentConv->input_message()) {
			return ret;
		}
		user_lock.lock();
		if(0 == cmd.paraIndex) {
			if(FUNC_SUCC != currentConv->send_message()) {
				errorString estr("send message failed.\n");
				stdout<<estr;
				throw fetionException("send message failed.");
			}
		}
		else if(1 == cmd.paraIndex) {
			if(FUNC_SUCC != currentConv->send_sms_to_phone()) {
				errorString estr("send message failed.\n");
				stdout<<estr;
				throw fetionException("send message failed.");
			}
		}
		else {
			errorString estr("invalid command.\n");
			stdout<<estr;
			throw fetionException("invalid command.");
		}
		if(send_msg) {
			send_msg= false;	// this thread will end, set send_msg to false
			cin_cond.notify_one(); // notiry main loop to continue
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "send message to contact failed : %s", ex.what());	
	}
	return ret;
}

int currentUser::send_direct_sms(Command &cmd)
{
	int ret = FUNC_SUCC;
	int code;
	boost::mutex::scoped_lock user_lock(user_mutex);	// mutex for user information

	try {
		currentConv->new_conversation(NULL, sip);
		user_lock.unlock();	// let listening thread continue
		if(FUNC_SUCC != currentConv->input_message()) {
			return ret;
		}
		user_lock.lock();
		code = currentConv->send_direct_message(cmd.paraString);
		if(280 == code) {
			listenString lstr("mesage has been send to %s successfully.\n", cmd.paraString.c_str());
			stdout<<lstr;
			return ret;
		}
		else if(420 == code || 421 == code) {
			//input_verify_code();
		}
		else {
			listenString lstr("send message to %s failed.\n", cmd.paraString.c_str());
			stdout<<lstr;
			return ret;
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "send direct sms failed : %s.", ex.what());
	}
	return ret;
}

int currentUser::get_contact_information_by_no(const std::string &no)
{
	int ret = FUNC_SUCC;
	currentUser *user = currentUser::get_instance();
	FetionSip *sip = user->get_sip();
	std::string body, sipmsg;
	Contact *contact;

	try {
		if((contact = find_tmp_contact_by_string(no)) != NULL) {
			contact->print_information();
			return ret;
		}
		sip->setType(FetionSip::SIP_SERVICE);
		sip->addEventHeader(SIP_EVENT_GETCONTACTINFO);
		if(FUNC_SUCC != Contact::generate_get_info_body(body, no)) {
			throw fetionException("generate get contact's information body failed");
		}
		if(FUNC_SUCC != sip->get_send_string(sipmsg, body)) {
			throw fetionException("get sending string of sip failed.");
		}
		sip->send_sip_message(sipmsg);
	}
	catch(std::exception &ex) {
		WRITE_LOG(LOG_TRACE, "get contact's information by no failed.");
		ret = FUNC_FAIL;
		errorString estr("can't get contact's information.\n");
		stdout<<estr;
	}
	return ret;
}

void currentUser::cleanup_group_list()
{
	for(std::list<Group>::iterator iter = groupList.begin(); iter != groupList.end(); ) {
		if(iter->dirty == 0) {
			iter = groupList.erase(iter);
		}
		else {
			++iter;
		}
	}
}

void currentUser::cleanup_contact_list()
{
	for(std::list<Contact>::iterator iter = contactList.begin(); iter != contactList.end(); ) {
		if(iter->dirty == 0) {
			iter = contactList.erase(iter);
		}
		else {
			++iter;
		}
	}
}

void currentUser::print_contactlist()
{
	boost::mutex::scoped_lock lock(user_mutex);	
	std::list<Contact>::iterator iter;
	int groupId = -100;
	int index;
	
	sysinfoString sstr("\e[1000;1Hcontact list:\n"
		"==========================================================================\n"
		"index  name         state    phoneno        fetionID   signature\n");
	stdout<<sstr;
	for(iter = contactList.begin(), index = 1; iter != contactList.end(); ++iter, ++index) {
		if(iter->groupid != groupId) {
			sstr("--------------------------------%s--------------------------------\n",
				find_group_by_id(iter->groupid)->groupName.c_str());
			stdout<<sstr;
		}
		
/*std::cout<<iter->get_contact_name().c_str()<<std::endl;
std::cout<<state_map[iter->state].c_str()<<std::endl;
std::cout<<iter->mobileno.c_str()<<std::endl;
std::cout<<iter->sId.c_str()<<std::endl;
std::cout<<iter->impression.c_str()<<std::endl;
*/
		sstr("  %-5d%-12s\e[1000;21H%-10s\e[1000;30H%-11s\e[1000;45H%-10s\e[1000;56H%s\n", 
			index, iter->get_contact_name().c_str(), state_map[iter->state].c_str(), 
			iter->mobileno.empty()?"not published":iter->mobileno.c_str(), 
			iter->sId.c_str(), iter->impression.c_str());
		stdout<<sstr;
		groupId = iter->groupid;
	}
	sstr("==========================================================================\n");
	stdout<<sstr;
}

